﻿#region File Description

//-----------------------------------------------------------------------------
// UserController.cs
//
// Author: ITU Team3 (2012)
//-----------------------------------------------------------------------------

#endregion File Description

#region Using Statements

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using rentit03_client.Models;
using rentit03_client.rentit03;

#endregion

namespace rentit03_client.Controllers
{
    public class UserController : RentitController
    {
        readonly ServiceClient _service = new ServiceClient();

        /// <summary>
        /// Gets the user with the specified user ID as a <seealso cref="UserObjct"/>.
        /// </summary>
        /// <param name="id">The id of the user to get.</param>
        /// <returns>The <seealso cref="UserObj"/> containing the user data.</returns>
        private UserObj ReadUser(int id = -1)
        {
            var r = id <= 0 ? _service.ReadUserFromSession(Sid) : _service.ReadUser(id, Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);
            if (ro.Success)
            {
                var user = MagicJSON.Deserialize<UserObj>(ro.Value);
                return user;
            }
            return new UserObj();
        }

        /// <summary>
        /// Gets the view of the user with the specified user ID.
        /// </summary>
        /// <param name="id">The id of the specififed user.</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the user data view</returns>
        public ActionResult Index(int id = -1)
        {
            var user = id >= 0 ? ReadUser(id) : User;
            ViewBag.ViewedUser = user;
            ViewBag.Title = user.Name;

            return View();
        }

        /// <summary>
        /// Logout the current user if one is logged in.
        /// </summary>
        /// <returns>The <seealso cref="ActionResult"/> containing the logout action</returns>
        public ActionResult Logout()
        {
            var r = _service.Logout(Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            Sid = String.Empty;

            return RedirectToAction("Index", "Home");
        }

                public ActionResult Login()
        {
            if (!String.IsNullOrWhiteSpace(Sid))
                return RedirectToAction(String.Empty);

            return View(new UserViewModel());
        }

        /// <summary>
        /// Login a user using a <seealso cref="UserViewModel"/> containing the user login data.
        /// </summary>
        /// <param name="model">THe <seealso cref="UserViewModel"/> containing the user login data.</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the login result.</returns>
        [HttpPost]
        public ActionResult Login(UserViewModel model)
        {
            if (!String.IsNullOrWhiteSpace(Sid))
                return RedirectToAction("Index");

            if (String.IsNullOrWhiteSpace(model.Email))
            {
                ModelState.AddModelError("Email", "Email can't be empty");
                return View(model);
            }

            if (String.IsNullOrWhiteSpace(model.Password))
            {
                ModelState.AddModelError("Password", "Password can't be empty");
                return View(model);
            }

            var r = _service.Login(model.Email, model.Password);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            // Check if everything went well
            if (!ro.Success)
            {
                switch (ro.Status)
                {
                    case 101:
                        ModelState.AddModelError("Email", ro.Message);
                        break;

                    default:
                        // TODO: Change to ro.Value
                        ModelState.AddModelError("Email", "Error: " + r);
                        break;
                }

                return View(model);
            }

            Sid = ro.Value;

            // Redirect
            return RedirectToAction("Index", "Home");
        }

        // GET: /Comment/Create

        /// <summary>
        /// Creates a new <seealso cref="UserViewModel"/> containing the user data.
        /// </summary>
        /// <returns>The <seealso cref="ActionResult"/> containing the data of the created user.</returns>
        public ActionResult Create()
        {
            if (!String.IsNullOrWhiteSpace(Sid))
                return RedirectToAction(String.Empty);

            ViewBag.Title = "Create user";
            return View("Form", new UserViewModel());
        }

        //
        // POST: /Comment/Create

        /// <summary>
        /// Edits or creates a user.
        /// </summary>
        /// <param name="model">The <seealso cref="UserViewModel"/> to save changes to.</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the save action´.</returns>
        [HttpPost]
        public ActionResult Save(UserViewModel model)
        {
            var creating = model.UserId <= 0;

            if (creating && !String.IsNullOrWhiteSpace(Sid))
                return RedirectToAction(String.Empty);

            if (!model.Password.Equals(model.ConfirmPassword))
            {
                ModelState.AddModelError("Password", "The password should be the same in both fields");
                return View("Form", model);
            }

            // Create new user object
            var user = new UserObj
            {
                Id = 0,
                Email = model.Email,
                Password = model.Password,
                Name = model.Name,
                PhoneNumber = model.Phone,
                Address = model.Address
            };

            var r = creating ? _service.CreateUser(user.ToJSON(), String.Empty) : _service.EditUser(model.UserId, user.ToJSON(), Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            // Check if everything went well
            if (!ro.Success)
            {
                switch (ro.Status)
                {
                    case 200:
                    case 201:
                    case 202:
                    case 204:
                        ModelState.AddModelError("Email", ro.Message);
                        break;

                    case 203:
                        ModelState.AddModelError("Password", ro.Message);
                        break;

                    default:
                        // TODO: Change to ro.Value
                        ModelState.AddModelError("Email", "Error: " + r);
                        break;
                }

                return View("Form", model);
            }

            if (creating)
            {
                r = _service.Login(model.Email, model.Password);
                ro = MagicJSON.Deserialize<ReturnObj>(r);
                Sid = ro.Value;
            }

            // Return to user profile
            return RedirectToAction("Index");
        }

        /// <summary>
        /// Deletes the user with the specified user id. Must match the logged in user. Admins can delete any user.
        /// </summary>
        /// <param name="id">The id of the user do delete. id = 1 or 0 does not work.</param>
        /// <param name="delete">Specify whether to delete the user with the specified user ID</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the delete result.</returns>
        public ActionResult Delete(int id, bool delete = false)
        {
            // You can't delete without being logged in (sid is not null or empty)
            // You can delete all users (but 0 and 1) if you have permission DeleteUser
            // You can delete the user if it's you (id is equal User.Id or id is less than 0)
            if (String.IsNullOrWhiteSpace(Sid))
                return RedirectToAction("Login");

            if (!User.Permissions.Contains("DeleteUser") && User.Id != id || id == 1 || id == 0)
                return RedirectToAction("Index", "User");

            if (delete)
            {
                var r = new ServiceClient().DeleteUser(id, Sid);
                var ro = MagicJSON.Deserialize<ReturnObj>(r);

                if (ro.Success)
                    return (User.Id == id) ? RedirectToAction("Logout") : RedirectToAction("Overview");

                ViewBag.message = ro.Message;
            }

            ViewBag.movieTitle = ReadUser(id).Name;

            ViewBag.id = id;
            return View();
        }

        /// <summary>
        /// Edits the user with the specified user id. Must match the logged in user. Admins can edit any user.
        /// </summary>
        /// <param name="id">The id of the user to edit.</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the updated editted user data.</returns>
        public ActionResult Edit(int id = -1)
        {
            // You can't edit without being logged in (sid is not null or empty)
            // You can edit all users (but 0 and 1) if you have permission EditUser
            // You can edit the user if it's you (id is equal User.Id or id is less than 0)

            if (String.IsNullOrWhiteSpace(Sid))
                return RedirectToAction("Login");

            if (!User.Permissions.Contains("EditUser") && User.Id != id || id == 1 || id == 0 || id >= 0)
                return RedirectToAction("Index", "User");
            
            var user = ReadUser(id);

            var model = new UserViewModel
            {
                UserId = user.Id,
                Email = user.Email,
                Name = user.Name,
                Phone = user.PhoneNumber,
                Address = user.Address
            };

            ViewBag.Title = "Edit user";

            return View("Form", model);
        }

        /// <summary>
        /// Gets the number of users matching the search parameters of the <seealso cref="OverviewUserParemObj"/>.
        /// </summary>
        /// <param name="searchObj">The <seealso cref="OverviewUserParemObj"/> containing the search parameters.</param>
        /// <returns>The number of users matching the specified search parameters.</returns>
        private static int OverviewCount(OverviewUserParemObj searchObj)
        {
            var r = new ServiceClient().OverviewUserCount(searchObj.ToJSON(), Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            return (ro.Success) ? Convert.ToInt32(ro.Value) : 0;
        }

        /// <summary>
        /// Returns the <seealso cref="ActionResult"/> containing the specified number of pages at the specified size used for displaying users.
        /// </summary>
        /// <param name="page">The number of pages to use.</param>
        /// <param name="count">The number of users to display on each page.</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the specified number of pages.</returns>
        public ActionResult Overview(int page = 1, int count = 20)
        {
            var search = new OverviewUserParemObj { Page = page - 1, Count = count };

            var max = OverviewCount(search);
            ViewBag.Pager = new Pager { Current = page, Count = count, PageCount = (int)Math.Ceiling((double)(max) / count), Action = "Overview", Controller = "User" };

            ViewBag.Title = "Users";
            ViewBag.Users = Search(search);
            return View();
        }

        /// <summary>
        /// Returns a list of <seealso cref="UserObj"/> matching the specified search parameters of the <seealso cref="OverviewUserParemObj"/>.
        /// </summary>
        /// <param name="searchObj">The <seealso cref="OverviewUserParemObj"/> containing the search parameters.</param>
        /// <returns>The list of <seealso cref="UserObj"/> matching the specified search parameters-</returns>
        private static List<UserObj> Search(OverviewUserParemObj searchObj = default(OverviewUserParemObj))
        {
            var r = new ServiceClient().OverviewUsers(searchObj.ToJSON(), Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            return (ro.Success) ? MagicJSON.Deserialize<List<UserObj>>(ro.Value) : new List<UserObj>();
        }
    }

    /// <summary>
    /// Contains user info.
    /// </summary>
    public class UserViewModel
    {
        public int UserId { get; set; }

        [Required]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Confirm Password")]
        public string ConfirmPassword { get; set; }

        [Required]
        public string Name { get; set; }

        [Required]
        [DataType(DataType.PhoneNumber)]
        [Range(3, 14)]
        [Display(Name = "Phone Number")]
        public string Phone { get; set; }

        [Required]
        [DataType(DataType.MultilineText)]
        public string Address { get; set; }
    }
}